﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Media;
using MyFloridaHouse.libSvBingMapsMultiShape;
using MyFloridaHouse.libSvCommonUtilities;
using MyFloridaHouse.libSvOpenDistricts.Census;
using MyFloridaHouse.libSvOpenDistricts.MapShapes;
using MyFloridaHouse.libSvOpenDistricts.SummaryDataGrid;
using MyFloridaHouse.ServiceConn.DistrBldrSvcRef;


namespace MyFloridaHouse.libSvOpenDistricts.Districts
{
    [DataContract(IsReference=true)]
    [KnownType(typeof(CensusGeometryNode))]
    
    public partial class DistrictPlan : INotifyPropertyChanged
    {

        #region INotifyPropertyChanged Members
        public event PropertyChangedEventHandler PropertyChanged;
        protected void NotifyPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }
        #endregion


        private bool _isCensusGeometryInitializeCompleted = false;
        public bool isCensusGeometryInitializeCompleted
        {
            get { return _isCensusGeometryInitializeCompleted; }
            set
            {
                if (_isCensusGeometryInitializeCompleted != value)
                {
                    _isCensusGeometryInitializeCompleted = value;
                    if (value)
                    {
                        NotifyPropertyChanged("isCensusGeometryInitializeCompleted");
                    }
                }
            }
        }

        private bool _isShowDistrictRibbonSelected = true;
        public bool isShowDistrictRibbonSelected
        {
            get { return _isShowDistrictRibbonSelected; }
            set 
            { 
                if (_isShowDistrictRibbonSelected != value) 
                { 
                    _isShowDistrictRibbonSelected = value;
                    NotifyPropertyChanged("isShowDistrictRibbonSelected");
                    NotifyPropertyChanged("isDistrictRibbonVisible");
                } 
            }
        }

        private bool _isShowDistrictStragglerRibbonSelected = false;
        public bool isShowDistrictStragglerRibbonSelected
        {
            get { return _isShowDistrictStragglerRibbonSelected; }
            set 
            { 
                if (_isShowDistrictStragglerRibbonSelected != value) 
                { 
                    _isShowDistrictStragglerRibbonSelected = value;
                    NotifyPropertyChanged("isShowDistrictStragglerRibbonSelected");
                    NotifyPropertyChanged("isDistrictStragglerRibbonVisible");
                    NotifyPropertyChanged("isDistrictExtentVisible");
                } 
            }
        }

        public bool isDistrictRibbonVisible
        {
            get { return ((districtCount > 0) & isShowDistrictRibbonSelected); }
        }

        public bool isDistrictStragglerRibbonVisible
        {
            get { return (isShowDistrictStragglerRibbonSelected); }
        }

        public bool isDistrictExtentVisible
        {
            get
            {
                if (selectedDistrictSubShapeList == null)
                {
                    return false;
                }
                else
                {
                    return ((selectedDistrictSubShapeList.Count() > 0));
                }
            }
        }

        private string _districtPlanName = "District Plan Name";
        public string districtPlanName
        {
            get { return _districtPlanName; }
            set
            {
                if (_districtPlanName != value)
                {
                    _districtPlanName = value;
                    NotifyPropertyChanged("districtPlanName");
                }
            }
        }

        private string _districtPlanFileName = "";
        public string districtPlanFileName
        {
            get { return _districtPlanFileName; }
            set
            {
                if (_districtPlanFileName != value)
                {
                    _districtPlanFileName = value;
                    NotifyPropertyChanged("districtPlanFileName");
                }
            }
        }

        private string _districtPlanDescription = "District Plan Description";
        public string districtPlanDescription
        {
            get { return _districtPlanDescription; }
            set
            {
                if (_districtPlanDescription != value)
                {
                    _districtPlanDescription = value;
                    NotifyPropertyChanged("districtPlanDescription");
                }
            }
        }

        private string _districtPlanTypeName = "";
        public string districtPlanTypeName
        {
            get { return _districtPlanTypeName; }
            set
            {
                if (_districtPlanTypeName != value)
                {
                    _districtPlanTypeName = value;
                    NotifyPropertyChanged("districtPlanTypeName");
                }
            }
        }

        private int _districtCount;
        public int districtCount
        {
            get { return _districtCount; }
            set 
            { 
                if (_districtCount != value) 
                { 
                    _districtCount = value;
                    NotifyPropertyChanged("districtCount");
                    NotifyPropertyChanged("isDistrictRibbonVisible");
                    NotifyPropertyChanged("isDistrictStragglerRibbonVisible");
                    NotifyPropertyChanged("isDistrictExtentVisible");
                    NotifyPropertyChanged("selectedDistrictSubShapeList");
                    NotifyPropertyChanged("selectedDistrictHoleList");
                } 
            }
        }

        private ObservableCollection<District> _districtList = new ObservableCollection<District>();
        public ObservableCollection<District> districtList
        {
            get { return _districtList; }
            set
            {
                if (_districtList != value)
                {
                    _districtList = value;
                    NotifyPropertyChanged("districtList");
                    NotifyPropertyChanged("selectedDistrictSubShapeList");
                }
            }
        }

        //
        private ObservableCollection<DistrictSubShapeMetadata> _selectedDistrictSubShapeList = new ObservableCollection<DistrictSubShapeMetadata>();
        public ObservableCollection<DistrictSubShapeMetadata> selectedDistrictSubShapeList
        {
            get
            {
                return _selectedDistrictSubShapeList;
            }
            set
            {
                _selectedDistrictSubShapeList = value;
                NotifyPropertyChanged("selectedDistrictSubShapeList");
                NotifyPropertyChanged("isDistrictExtentVisible");
            }
        }

        private ObservableCollection<DistrictSubShapeMetadata> _selectedDistrictHoleList = new ObservableCollection<DistrictSubShapeMetadata>();
        public ObservableCollection<DistrictSubShapeMetadata> selectedDistrictHoleList
        {
            get
            {
                return _selectedDistrictHoleList;
            }
            set
            {
                _selectedDistrictHoleList = value;
                NotifyPropertyChanged("selectedDistrictHoleList");
            }
        }

        public BoundingBoxDTO selectedDistrictBoundingBox
        {
            get { return districtList[selectedDistrict].boundingBox; }
        }



        private int _districtSelectedIndex = -1;
        public int districtSelectedIndex
        {
            get { return _districtSelectedIndex; }
            set
            {
                if (_districtSelectedIndex != value)
                {
                    _districtSelectedIndex = value;
                    if (districtSelectedIndex > -1)
                    {
                        foreach (District dist in districtList)
                        {
                            dist.isSelected = false;
                        }
                        districtList[districtSelectedIndex].isSelected = true;
                    }

                    UpdateSelectedDistrictSubShapeList();

                    NotifyPropertyChanged("districtSelectedIndex");
                    NotifyPropertyChanged("selectedDistrict");
                    NotifyPropertyChanged("isDistrictRibbonVisible");
                    NotifyPropertyChanged("districtSelectedFillColorBrush");
                    NotifyPropertyChanged("isDistrictStragglerRibbonVisible");
                    NotifyPropertyChanged("isDistrictExtentVisible");
                }
            }
        }

        public SolidColorBrush districtSelectedFillColorBrush
        {
            get
            {
                if ((districtSelectedIndex >= 0) & (districtSelectedIndex < districtList.Count()))
                {
                    return DistrictColors.GetBrushFromIndex(districtList[districtSelectedIndex].districtID);
                }
                else
                {
                    return new SolidColorBrush(Colors.Transparent);
                }
            }
        }

        // indicates what district would be affected if a district action is taken
        public int selectedDistrict
        {
            get
            {
                if (districtList == null)
                {
                    return -1;
                }
                if ((districtSelectedIndex >= 0) & (districtSelectedIndex <= districtList.Count))
                { return districtList[districtSelectedIndex].districtID; }
                else
                { return -1; }
            }
            set
            { // should throw an error here
                MessageBox.Show("Can't set selected district, should be read only");
            }
        }

        // indicates what district is being pointed at
        private string _currentDistrict = "Unassigned";
        public string currentDistrict
        {
            get { return _currentDistrict; }
            set
            {
                if (_currentDistrict != value)
                {
                    _currentDistrict = value;
                    NotifyPropertyChanged("currentDistrict");
                }
            }
        }





        private int _districtPlanTargetPopulation = Constants.TOTAL_POPULATION_TO_BE_APPORTIONED;
        public int districtPlanTargetPopulation
        {
            get { return _districtPlanTargetPopulation; }
            set
            {
                if (_districtPlanTargetPopulation != value)
                {
                    _districtPlanTargetPopulation = value;

                    NotifyPropertyChanged("districtPlanTargetPopulation");
                }
            }
        }

        private bool _districtLockAllState = false;
        public bool districtLockAllState
        {
            get { return _districtLockAllState; }
            set
            {
                if (_districtLockAllState != value)
                {
                    _districtLockAllState = value;

                    NotifyPropertyChanged("districtLockAllState");
                }
            }
        }




        public DistrictPlan_XML xmlDistrictPlan {get; set;}

        private DistrictPlanTypes districtPlanType;

        public string districtPlanTypeID = "";



        private DistrictClipboard _districtClipboard = new DistrictClipboard();
        private DistrictClipboard districtClipboard { get; set; }

        private List<int> _changedDistrictsList = new List<int>();
        public List<int> changedDistrictsList
        {
            get { return _changedDistrictsList; }
            set { _changedDistrictsList = value; }
        }

        private CensusGeometry _censusGeometry = null;
        public CensusGeometry censusGeometry
        {
            get { return _censusGeometry; }
            set { _censusGeometry = value; }
        }

        private DistrictLabel _districtLabel = null;
        public DistrictLabel districtLabel
        {
            get { return _districtLabel; }
            set { _districtLabel = value; }
        }

        private DistrictFill _districtFill = null;
        public DistrictFill districtFill
        {
            get { return _districtFill; }
            set { _districtFill = value; }
        }

        private MapShapeLayer districtMapLayer = null;

        public DistrictPlan(MapShapeLayer districtMapLayer, CensusGeometry censusGeometry, DistrictLabel districtLabel, DistrictFill districtFill)
        {
            this.censusGeometry = censusGeometry;
            this.districtMapLayer = districtMapLayer;
            this.districtLabel = districtLabel;
            this.districtFill = districtFill;
        }


        public void InitializeDistrictPlan(string newDistrictPlanName, string newPlanDescription, int newDistrictCount, string newDistrictPlanTypeID, string newDistrictPlanTypeName)
        {
            //todo: Reset the snapshot file when we start a new district plan

            // reset 
            districtCount = newDistrictCount;
            districtPlanName = newDistrictPlanName;
            districtPlanDescription = newPlanDescription;
            districtPlanTypeID = newDistrictPlanTypeID;
            districtPlanTypeName = newDistrictPlanTypeName;

            districtList = new ObservableCollection<District>();
            for (var ndx = 0; ndx <= newDistrictCount; ndx++)
            {
                District newDistrict = new District(ndx, ndx.ToString());
                districtList.Add(newDistrict);
            }
            // indicate that district 0 is the default district for all unselected geometries
            districtList[0].displayName = "Unassigned";
            districtSelectedIndex = 1;

            selectedDistrictSubShapeList = new ObservableCollection<DistrictSubShapeMetadata>();
            selectedDistrictHoleList = new ObservableCollection<DistrictSubShapeMetadata>();
            districtClipboard = new DistrictClipboard();
            changedDistrictsList = new List<int>();


            censusGeometry.ResetDistrictAll(districtCount);
            SummaryDataGridManager.Initialize(newDistrictCount);
            SummaryDataGridManager.SummaryDataGridDataChanged += new SummaryDataGridDataChangedEventHandler(districtLabel.OnSummaryDataGridDataChanged);
            isShowDistrictRibbonSelected = true;
        }



        void MemberPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            NotifyPropertyChanged(e.PropertyName);
        }

        


        public void GetDistrictsInView()
        {
            if (districtList != null)
            {
                // determine the districts in view and tell the map
                List<int> newRegionsIDsWithoutShapes = districtMapLayer.SaveRegionsInView(GetDistrictIDsInView());

                LoadRegionList(newRegionsIDsWithoutShapes);

            }
        }


        public List<int> GetDistrictIDsInView()
        {
            List<int> districtsInViewList = new List<int>();
            foreach (var dist in districtList)
            {
                // if there is no shape, there is no district
                if (dist.mapMultiShapeDTO != null)
                {
                    if (districtMapLayer.IsRegionInView(dist.boundingBox))
                    {
                        districtsInViewList.Add(dist.districtID);
                    }
                }
            }
            return districtsInViewList;
        }


        public List<RegionDetails> GetDistrictListDetails(List<int> newRegionsIDsWithoutShapes)
        {
            //todo: can modify region details to customize outline for each region on a layer
            List<RegionDetails> districtDetailsList = new List<RegionDetails>();
            foreach (var distID in newRegionsIDsWithoutShapes)
            {
                districtDetailsList.Add(districtMapLayer.regionDetails);
            }
            return districtDetailsList;
        }


        public bool LoadDistrictPlanFromDOJ(StreamReader importFileStreamReader,  int districtCount )
        {
            return censusGeometry.FromDOJ(importFileStreamReader, districtCount);
        }

        public string GetDistrictFromRegionID(int layerID, int regionID)
        {
            return censusGeometry.GetDistrictFromRegionID(layerID, regionID);
        }

        #region clipboard helpers (Cut, Copy, Paste)

        public bool DistrictCutCanExecute()
        {
            if ((districtSelectedIndex > 0) & (districtSelectedIndex < districtList.Count()))
            {
                if (districtList[districtSelectedIndex].isLocked == false)
                {
                    return true;
                }
            }
            return false;
        }
        
        public bool DistrictCut()
        {
            List<UOG> uogIDList = GetDistrictUogIDs(districtCount, districtSelectedIndex);
            if (MoveShapesToNewDistrict(Constants.UnassignedDistrictID, uogIDList))
            {
                districtClipboard.SaveDistrictsToClipboard(uogIDList);
                return true;
            }
            return false;
        }

        public bool DistrictCopyCanExecute()
        {
            if (districtSelectedIndex > 0)
            {
                return true;
            }
            return false;
        }

        public void DistrictCopy()
        {
            List<UOG> uogIDList = GetDistrictUogIDs(districtCount, districtSelectedIndex);
            districtClipboard.SaveDistrictsToClipboard(uogIDList);

        }

        public bool DistrictPasteCanExecute()
        {
            if ((districtSelectedIndex > 0) & (districtSelectedIndex < districtList.Count()))
            {
                if (districtList[districtSelectedIndex].isLocked == false)
                {
                    return true;
                }
            }
            return false;

        }
        public bool DistrictPaste()
        {
            List<UOG> uogIDList = districtClipboard.LoadDistrictsFromClipboard();
            return MoveShapesToNewDistrict(districtSelectedIndex, uogIDList);
        }


        #endregion


        public void ToggleDistrictLockAll()
        {

            districtLockAllState = !districtLockAllState;

            for (int ndx = 1; ndx < districtList.Count; ndx++)
            {
                districtList[ndx].isLocked = districtLockAllState;
            }

            censusGeometry.SetDistrictLockAll(districtLockAllState);
        }




        public void ToggleSelectedDistrictLock()
        {
            if (districtList[districtSelectedIndex].isLocked == true)
            {
                districtList[districtSelectedIndex].isLocked = false;
            }
            else
            {
                districtList[districtSelectedIndex].isLocked = true;
            }

            censusGeometry.SetLockByDistrict(selectedDistrict, districtList[districtSelectedIndex].isLocked);

        }


        internal void UpdateDistrictLocks()
        {
            foreach (var dist in districtList)
            {
                censusGeometry.SetLockByDistrict(dist.districtID, dist.isLocked);
            }
        }

        public bool MoveShapesToNewDistrict(int targetDistrictID, List<UOG> uogIDList)
        {
            if (districtList[targetDistrictID].isLocked)
            {
                MessageBox.Show("Target District is Locked");
                return false;
            }

            if (uogIDList == null)
            {
                // couldn't find any shapes, maybe the lasso got messed up
                // if no uogIDs were found, maybe the user selected a non-florida region
                MessageBox.Show("Could not find any shapes within Florida, please try again.");
            }
            else if (uogIDList.Count == 0)
            {
                // couldn't find any shapes, maybe the lasso got messed up
                // if no uogIDs were found, maybe the user selected a non-florida region
                MessageBox.Show("Could not find any shapes , please try again.");
            }
            else
            {

                MoveUogIDsToDistrict(targetDistrictID, uogIDList);
                return true;
            }
            return false;
        }

        public void SetActiveDistrict(int districtID)
        {
            districtSelectedIndex = districtID;
            UpdateSelectedDistrictSubShapeList();  //TODO: After a district button click, SetActiveDistrict calls UpdateSelectedDistrictSubShapeList probably should be done with an event (but I'm not sure).
        }

        public void UpdateDistrictMetaData(DistrictShapeSTO districtShapeSTO)
        {
            int districtID = districtShapeSTO.districtID;

            bool isContiguous = districtShapeSTO.districtSubShapeList.Count <= 1;

            districtList[districtID].lastChangedTimeStamp = DateTime.Now;
            districtList[districtID].boundingBox = districtShapeSTO.districtShapeBoundingBox;
            districtList[districtID].centerPoint = districtShapeSTO.centerPoint;
            districtList[districtID].subShapeMetadataList = DistrictSubShapeMetadata.GetSubShapeMetadata(isContiguous, districtShapeSTO.districtSubShapeList);
            districtList[districtID].subShapeHoleMetadataList = DistrictSubShapeMetadata.GetSubShapeHoleMetadata(districtShapeSTO.districtHolesList);
            districtList[districtID].isContiguous = isContiguous;
            districtList[districtID].hasHole = (districtList[districtID].subShapeHoleMetadataList.Count > 0);
            UpdateSelectedDistrictSubShapeList();
        }

        public void UpdateDistrictShape(int districtID, libSvDTO.MapMultiShapeDTO newMapMultiShapeDTO)
        {
            districtList[districtID].mapMultiShapeDTO = newMapMultiShapeDTO;
            districtList[districtID].districtMultiMapShape = MapShapeTools.BuildShape(districtID, newMapMultiShapeDTO);
        }

        public void ClearDistrictShape(int districtID)
        {
            // we've adjusted census geometry with new geography, wipe out the old shape information
            districtList[districtID].mapMultiShapeDTO = new libSvDTO.MapMultiShapeDTO();
            districtList[districtID].districtMultiMapShape = null;
        }

        public void UpdateSelectedDistrictSubShapeList()
        {
            if (selectedDistrictSubShapeList != null)
            {
                selectedDistrictSubShapeList.Clear();
                selectedDistrictHoleList.Clear();
                if ((selectedDistrict >= 0) & (selectedDistrict < districtList.Count()))
                {
                    if (districtList[selectedDistrict].subShapeMetadataList != null)
                    {
                        foreach (var subShape in districtList[selectedDistrict].subShapeMetadataList)
                        {
                            selectedDistrictSubShapeList.Add(subShape);
                        }

                        foreach (var subShape in districtList[selectedDistrict].subShapeHoleMetadataList)
                        {
                            selectedDistrictHoleList.Add(subShape);
                        }



                        // need to force nonContiguous list property notification because it is derived, no actual change signals View to update
                        NotifyPropertyChanged("selectedDistrictSubShapeList");
                        NotifyPropertyChanged("selectedDistrictHoleList");

                    }
                }
            }

        }

        public List<UOG> GetDistrictUogIDs(int maxDistricts, int cutDistrictID)
        {
            List<UOG> uogIDList = new List<UOG>();

            // get the condensed list of UogIDs that make up this district
            List<string> condensedList = CensusGeometryCondensed.ToCondensedList(maxDistricts);

            // make an assumption that the selected district is under maxDistricts
            foreach (var uogID in condensedList[cutDistrictID].Split(','))
            {
                if (uogID.Length > 0)
                {
                    uogIDList.Add(new UOG(Convert.ToInt32(uogID)));
                }
            }

            return uogIDList;
        }

        public bool UndoChanges()
        {
            // We know everything in the change list is a census block, our base assignment unit
            List<CensusGeometryHistoryChange> changeList = censusGeometry.history.GetChangeList();

            // first get a list of all the districts they used to be in, plus the one they are in now
            changedDistrictsList.Clear();

            // old districts
            int originalDistrictID = 0;  // worst case, we'll put everything to unassigned
            var originalDistrictQuery = (from chng in changeList
                                         select chng.oldDistrictID).Distinct();
            foreach (var oldDistrictID in originalDistrictQuery)
            {
                if (!changedDistrictsList.Contains(oldDistrictID))
                {
                    changedDistrictsList.Add(oldDistrictID);
                    originalDistrictID = oldDistrictID;
                }
            }

            // current districts  (This better be one and only 1 district)
            var currentlDistrictQuery = (from chng in changeList
                                         select chng.newDistrictID).Distinct();
            foreach (var newDistrictID in currentlDistrictQuery)
            {
                if (!changedDistrictsList.Contains(newDistrictID))
                {
                    changedDistrictsList.Add(newDistrictID);
                }
            }


            foreach (var changedDistrictID in changedDistrictsList)
            {
                // then for each district, build its uogIDList
                var uogIDList = from chng in changeList
                                where chng.oldDistrictID == changedDistrictID
                                select chng.uogID;

                // then for each district, put those uogIDs back in the district they used to be
                


                List<int> blockOnlyUogIDList = censusGeometry.convertIntUogIDListToBlocksOnly(uogIDList.ToList());
                censusGeometry.AssignBlockOnlyUogListToDistrict(changedDistrictID, blockOnlyUogIDList);

            }

            censusGeometry.history.Reset();

            Snapshot.Save(districtPlanName, districtPlanTypeName, districtCount, CensusGeometryCondensed.ToCondensedList(districtCount));
            return true;
        }


        public void MoveUogIDsToDistrict(int targetDistrictID, List<UOG> uogIDList)
        {
            try
            {
                // Convert the uogIDList to a list of blocks nodes
                var blockList = censusGeometry.GetCensusBlocksFromUogIDList(uogIDList);

                // exclude any locked nodes
                var unlockedBlockList =(from node in blockList
                                        where false == node.isLocked 
                                        select node).ToList();

                // get changedDistricts that aren't  unassigned or multi-district so we can rebuild them later
                changedDistrictsList.Clear();
                var blockListChangedDistrictsList = (from node in unlockedBlockList
                                                     where CensusGeometryConstants.unassignedDistrictID !=  node.districtID
                                                         & CensusGeometryConstants.multiDistrictID != node.districtID
                                                     select node.districtID).Distinct().ToList();
                foreach (var districtID in blockListChangedDistrictsList)
                {
                    changedDistrictsList.Add(districtID);
                }
                if (!changedDistrictsList.Contains(targetDistrictID))
                {
                    changedDistrictsList.Add(targetDistrictID);
                }

                // special case: easy un-assign if all regions selected already belongs to target district, unassign it.
                if ((from node in unlockedBlockList
                      where node.districtID != targetDistrictID
                      select node.districtID).Count() == 0)
                {
                    targetDistrictID = Constants.UnassignedDistrictID;
                }

                // build a list of integer uogIDs to be reassigned
                List<int> intDistrictUogIDList = new List<int>();
                foreach (var uog in unlockedBlockList)
                {
                    intDistrictUogIDList.Add(uog.uogID);
                }

                // now reassign all the selected shapes to the target district
                censusGeometry.history.ChangeStarting();
                censusGeometry.AssignBlockOnlyUogListToDistrict(targetDistrictID, intDistrictUogIDList);
                censusGeometry.history.ChangeComplete();

                Snapshot.Save(districtPlanName, districtPlanTypeName, districtCount, CensusGeometryCondensed.ToCondensedList(districtCount));
            }
            catch (Exception ex)
            {
                throw ex;
            }

        }

        internal void ShowDistricts()
        {
            GetDistrictsInView();
        }

        internal void ShowNewDistrictShape(int districtID)
        {
            // if district is in view, then show it
            // build a list of 1 region
            if (districtMapLayer.IsRegionInView(districtList[districtID].boundingBox))
            {
                List<int> regionIDsList = new List<int>();
                regionIDsList.Add(districtID);
                LoadRegionList(regionIDsList);
            }
        }

        private void LoadRegionList(List<int> newRegionsIDsWithoutShapes)
        {
            // get the shapes for the districts in view
            List<MapRegionDTO> regionShapeList = (from dist in districtList
                                                  join rgnID in newRegionsIDsWithoutShapes on dist.districtID equals rgnID
                                                  select new MapRegionDTO
                                                  (
                                                      dist.districtID,
                                                      dist.districtID.ToString(), 
                                                      MapShapeTools.BuildShape(dist.districtID, MapMultiShapeTools.ReducePointsByZoomLevel(dist.mapMultiShapeDTO, districtMapLayer.zoomLevel)),
                                                      dist.centerPoint.longitude,
                                                      dist.centerPoint.latitude,
                                                      dist.boundingBox.northWest.latitude,
                                                      dist.boundingBox.northWest.longitude,
                                                      dist.boundingBox.southEast.latitude,
                                                      dist.boundingBox.southEast.longitude
                                                  )).ToList();

            // load the shape into the map
            districtMapLayer.LoadRegions(regionShapeList, GetDistrictListDetails(newRegionsIDsWithoutShapes), districtLabel);
        }

    }
}
